package com.baomidou.plugin.idea.mybatisx.dom.converter;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;

import com.baomidou.plugin.idea.mybatisx.util.JavaUtils;
import com.baomidou.plugin.idea.mybatisx.util.StringUtils;
import com.example.constant.Constants;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiClassType;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiType;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.PsiShortNamesCache;
import com.intellij.psi.search.searches.ClassInheritorsSearch;
import com.intellij.util.xml.ConvertContext;
import com.intellij.util.xml.GenericDomValue;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.Nullable;

/**
 * 加入命名空间检查, 当mapper.xml和mapper.class不匹配时,
 * 对mapper.xml的namespace标签标记红色下划线
 */
public class NamespaceConverter extends ConverterAdaptor<PsiClass> {
    /**
     * 转换成类
     *
     * @param id
     * @param context
     * @return
     */
    @Nullable
    @Override
    public PsiClass fromString(@Nullable @NonNls String id, ConvertContext context) {
        if (StringUtils.isEmpty(id)) {
            return null;
        }
        Optional<PsiClass> clazz = JavaUtils.findClazz(context.getProject(), id);
        return clazz.orElseGet(() -> findDaoInterfaceByNamespace(context.getProject(), id));
    }

    private static PsiClass findDaoInterfaceByNamespace(Project project, String namespace) {
        // 1. 查找所有继承BaseDao的类
        GlobalSearchScope scope = GlobalSearchScope.projectScope(project);
        PsiClass baseDao = null;
        for (final String s : Constants.BASE_DAO) {
            PsiClass[] baseDaos = PsiShortNamesCache.getInstance(project).getClassesByName(s, scope);
            baseDao = Arrays.stream(baseDaos).filter(PsiClass::isInterface).findFirst()
                .orElse(null);
            if (baseDao != null) {
                break;
            }
        }
        if (baseDao == null) {
            return null;
        }

        // 2. 搜索继承BaseDao的类 且泛型参数是namespace
        List<PsiClass> candidates = new ArrayList<>();
        ClassInheritorsSearch.search(baseDao, scope, true).forEach(candidate -> {
            for (PsiClassType type : candidate.getExtendsListTypes()) {
                PsiType[] parameters = type.getParameters();
                if (parameters.length > 0 && parameters[0].getPresentableText().equals(namespace)) {
                    candidates.add(candidate);
                }
            }
            return true;
        });
        // 优先接口
        // dao接口继承了baseDao
        final PsiClass psiClass = candidates.stream().filter(PsiClass::isInterface).findFirst()
            .orElse(null);
        if (psiClass != null) {
            return psiClass;
        }
        // daoImp 继承了 baseDaoImpl
        return candidates.stream().filter(e -> e.getInterfaces().length > 0)
            .map(e -> e.getInterfaces()[0]).findFirst().orElse(null);
    }

    @Override
    public void bindReference(GenericDomValue<PsiClass> genericValue, ConvertContext context, PsiElement newTarget) {
        if (newTarget instanceof PsiClass) {
            final PsiClass psiClass = (PsiClass) newTarget;
            final String qualifiedName = psiClass.getQualifiedName();
            genericValue.setStringValue(qualifiedName);
        }
    }

}
